/*
 * Copyright 2023 Google LLC
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef skgpu_graphite_geom_EdgeAAQuad_DEFINED
#define skgpu_graphite_geom_EdgeAAQuad_DEFINED

#include "include/core/SkRect.h"
#include "src/base/SkEnumBitMask.h"
#include "src/base/SkVx.h"
#include "src/gpu/graphite/geom/Rect.h"

namespace skgpu::graphite {
/* *
 * EdgeAAQuad contains (x,y) coordinates for the four corners of a quadrilateral, assumed to be
 * convex and in a consistent winding (CW vs. CCW is fine). Locally, the vertices are ordered
 * "top-left", "top-right", "bottom-right", "bottom-left". The edges are in order left (p0-p3),
 * top (p1-p0), right (p2-p1), and bottom (p3-p2).
 */
class EdgeAAQuad {
public:
    // SkEnumBitMask<Flags> is a typesafe equivalent to SkCanvas::QuadAAFlags.
    enum class Flags : uint8_t {
        kLeft = 0b0001,
        kTop = 0b0010,
        kRight = 0b0100,
        kBottom = 0b1000,

        kNone = 0b0000,
        kAll = 0b1111,
    };

    EdgeAAQuad() = delete;

    EdgeAAQuad(const SkRect &rect, SkEnumBitMask<Flags> edgeFlags)
        : fXs{ rect.fLeft, rect.fRight, rect.fRight, rect.fLeft },
          fYs{ rect.fTop, rect.fTop, rect.fBottom, rect.fBottom },
          fEdgeFlags(edgeFlags),
          fIsRect(true)
    {}
    EdgeAAQuad(const Rect &rect, SkEnumBitMask<Flags> edgeFlags)
        : fXs{ skvx::shuffle<0, 2, 2, 0>(rect.ltrb()) },
          fYs{ skvx::shuffle<1, 1, 3, 3>(rect.ltrb()) },
          fEdgeFlags(edgeFlags),
          fIsRect(true)
    {}
    EdgeAAQuad(const SkPoint points[4], SkEnumBitMask<Flags> edgeFlags)
        : fXs{ points[0].fX, points[1].fX, points[2].fX, points[3].fX },
          fYs{ points[0].fY, points[1].fY, points[2].fY, points[3].fY },
          fEdgeFlags(edgeFlags),
          fIsRect(false)
    {}
    EdgeAAQuad(const skvx::float4 &xs, const skvx::float4 &ys, SkEnumBitMask<Flags> edgeFlags)
        : fXs(xs), fYs(ys), fEdgeFlags(edgeFlags), fIsRect(false)
    {}

    // The bounding box of the quadrilateral (not counting any outsetting for anti-aliasing).
    Rect bounds() const
    {
        if (fIsRect) {
            return Rect({ fXs[0], fYs[0] }, { fXs[2], fYs[2] });
        }

        Rect p0p1 = Rect::LTRB(skvx::shuffle<0, 2, 1, 3>(skvx::float4(fXs.lo, fYs.lo))).makeSorted();
        Rect p2p3 = Rect::LTRB(skvx::shuffle<0, 2, 1, 3>(skvx::float4(fXs.hi, fYs.hi))).makeSorted();
        return p0p1.makeJoin(p2p3);
    }

    // Access the individual elements of the quad data.
    const skvx::float4 &xs() const
    {
        return fXs;
    }
    const skvx::float4 &ys() const
    {
        return fYs;
    }
    SkEnumBitMask<Flags> edgeFlags() const
    {
        return fEdgeFlags;
    }

    bool isRect() const
    {
        return fIsRect;
    }

private:
    skvx::float4 fXs;
    skvx::float4 fYs;
    SkEnumBitMask<Flags> fEdgeFlags;
    bool fIsRect;
};

SK_MAKE_BITMASK_OPS(EdgeAAQuad::Flags)
} // namespace skgpu::graphite

#endif // skgpu_graphite_geom_EdgeAAQuad_DEFINED
